#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "avl.h"
#include "list.h"
#include "rdictionary.h"

static node_t **nodes = NULL;

static avl_t *parts_tree = NULL;
static int *word_parts = NULL;

void rdict_init(int dict_size)
{
    if (parts_tree == NULL) {
        parts_tree = avl_create(MAX_PART_SIZE);
        word_parts = (int *) calloc(dict_size, sizeof(int));
        assert(word_parts != NULL);

        rdict_word_count = 0;
        rdict_mark_count = 0;
    }
}

void rdict_delete()
{
    if (parts_tree != NULL) {
        free(nodes);
        nodes = NULL;
        avl_destroy(parts_tree);
        parts_tree = NULL;
        free(word_parts);
        word_parts = NULL;
        rdict_word_count = 0;
        rdict_mark_count = 0;
    }
}

static void tree_index(node_t *node, int *last_index)
{
    if (node == NULL)
        return;

    nodes[*last_index] = node;
    ++(*last_index);

    tree_index(node->left, last_index);
    tree_index(node->right, last_index);
}

void rdict_optimize()
{
    int last_index;

    if (nodes == NULL && parts_tree != NULL) {
        nodes = (node_t **) malloc(sizeof(node_t *) * parts_tree->size);
        last_index = 0;
        tree_index(parts_tree->root, &last_index);
        assert(last_index == parts_tree->size);
    }
}

/*
 * Adds a word in the list which stores all words the part belongs to.
 */
static void add_occurrence(node_t *node, int i)
{
    assert(node != NULL);

    if (node->data == NULL)
        list_init((list_t **) &node->data, i);
    else
        list_add((list_t **) &node->data, i);

    assert(node->data != NULL);
}

void rdict_add(const char *key)
{
    int length, i;
    node_t *parts_node;

    word_parts[rdict_word_count] = 0;

    if (parts_tree != NULL) {
        length = strlen(key);
        for (i = 0; i < length; i += MAX_PART_SIZE) {
            /* Stores each part and associate the complete word. */
            parts_node = avl_insert(parts_tree, key + i, NULL);
            add_occurrence(parts_node, rdict_word_count);
            word_parts[rdict_word_count]++;
        }
    }

    rdict_word_count++;
}

int rdict_mark(int index)
{
    if (nodes[index] == NULL)
        return 0;

    int count, word_index;
    data_t data;
    node_t *part_node;

    part_node = nodes[index];
    nodes[index] = NULL;
    data = part_node->data;
    avl_remove(parts_tree, part_node->key);

    count = 0;
    /* For all words the part belongs. */
    for (word_index = list_iter((list_t *) data); word_index != -1; word_index = list_iter(NULL))
    {
        /* Mark part. */
        word_parts[word_index]--;
        /* If all parts are marked. */
        if (word_parts[word_index] == 0)
        {
            /* One new word was found! */
            rdict_mark_count++;
            count++;
        }
    }
    list_free((list_t **) &data);

    return count;
}
